﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using System.Linq;
using System.Text;
using nanoCMS.Core;
using nanoCMS.Forum.Concrete;

namespace nanoCMS.Forum.Providers
{
    public class ForumProvider
    {
        private readonly WorldContext worldContext;
        private ForumEntities _entities; //DO NOT ACCESS DIRECTLY
        private ForumEntities Entities
        {
            get { return _entities ?? (_entities = new ForumEntities(worldContext.EntityConnectionString)); }
        }

        private ThreadProvider threadProvider; // TODO - remove!

        public ForumProvider(WorldContext worldContext)
        {
            this.worldContext = worldContext;
            this.threadProvider = new ThreadProvider(this.worldContext);
        }

        public Concrete.Forum GetForum(int forumID)
        {
            return Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
        }

        // TODO change name
        public void Add(string title, string description, int parentId, int tenantId)
        {
            var order = getMaxOrder(parentId);
            if(order < 0)
            {
                order = 0;
            }
            else
            {
                order = order + 1; 
            }
            // TODO check ParentForumID existance
            var forum = new Concrete.Forum()
            {
                Title = title,
                Description = description,
                ParentForumID = parentId,
                Order = order,
                TenantID = tenantId
            };
            Entities.Forums.AddObject(forum);
            Entities.SaveChanges();
        }

        public void Edit(int forumId, string title, string description, int parentForumId)
        {
            var forumEntity = Entities.Forums.Where(f => f.ForumID == forumId).SingleOrDefault();
            if(forumEntity == null)
            {
                return;
            }
            int oldParent = forumEntity.ParentForumID; 
            // put new data
            forumEntity.Title = title;
            forumEntity.Description = description;
            // forumParent != forumID - check recursion
            if (forumEntity.ForumID != parentForumId)
            {
                forumEntity.ParentForumID = parentForumId;
            }
            // preform reoder
            if(oldParent != parentForumId)
            {
                // put new order (last)
                int newOrder = getMaxOrder(parentForumId); 
                forumEntity.Order = newOrder;
                Entities.SaveChanges(); 
                // refresh old order level
                List<Concrete.Forum> oldForumLevelList = Entities.Forums.Where(f => f.ParentForumID == oldParent).OrderBy(
                    o => o.Order).ToList(); 
                for(int i=0; i < oldForumLevelList.Count; i++)
                {
                    oldForumLevelList[i].Order = i; 
                }
                Entities.SaveChanges(); 
            }
            else
            {
                Entities.SaveChanges(); 
            }
            
        }

        public List<Concrete.Forum> GetRootForumList()
        {
            return Entities.Forums.Where(f => f.ParentForumID == 0).OrderBy(o => o.Order).ToList();
        }

        public List<Concrete.Forum> GetAllForums()
        {
            return Entities.Forums.ToList();
        }

        public List<Concrete.Forum> GetSubForums(int forumID)
        {
            return Entities.Forums.Where(f => f.ParentForumID == forumID).
                        OrderBy(o => o.Order).ToList();
        }

        public void ChangeForumOrder(int forumID, bool up)
        {
            var forum = Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
            if (forum != null)
            {
                var forumLevelList = Entities.Forums.Where(f => f.ParentForumID == forum.ParentForumID).
                    OrderBy(o => o.Order).ToList();
                // find forum index
                int forumIndex = 0;
                foreach (var forumEntry in forumLevelList)
                {
                    if (forumEntry.ForumID == forum.ForumID)
                    {
                        break;
                    }
                    forumIndex++;
                }
                // up == -
                if (up)
                {
                    if (forumIndex > 0)
                    {
                        var tempOrder = forumLevelList[forumIndex - 1].Order;
                        forumLevelList[forumIndex - 1].Order = forumLevelList[forumIndex].Order;
                        forumLevelList[forumIndex].Order = tempOrder;
                    }
                }
                else
                {
                    // down == +
                    if (forumIndex != (forumLevelList.Count - 1))
                    {
                        var tempOrder = forumLevelList[forumIndex + 1].Order;
                        forumLevelList[forumIndex + 1].Order = forumLevelList[forumIndex].Order;
                        forumLevelList[forumIndex].Order = tempOrder;
                    }
                }
                // refresh status
                var orderedForums = forumLevelList.OrderBy(f => f.Order).ToList();
                //                    // remove from forums
                //                    foreach (var entry in forumLevelList)
                //                    {
                //                        context.Detach(entry);
                //                    }
                for (int i = 0; i < orderedForums.Count; i++)
                {
                    orderedForums[i].Order = i;
                    //                        context.AddObject("Forums", orderedForums[i]);
                    Entities.ObjectStateManager.ChangeObjectState(orderedForums[i], System.Data.EntityState.Modified);
                }
                Entities.DetectChanges();
                Entities.SaveChanges(SaveOptions.DetectChangesBeforeSave);
            }
        }

        public void IncreaseForumThreadCount(int forumID)
        {
            // update thread posts
            Concrete.Forum forum = Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
            if (forum == null)
            {
                throw new Exception(String.Format("No forum with ForumID = {0}.", forumID));
            }
            forum.ThreadsCount++; 
            Entities.ObjectStateManager.ChangeObjectState(forum, EntityState.Modified);
            Entities.SaveChanges();
        }

        public void DecreaseForumThreadCount(int forumID)
        {
            // update thread posts
            Concrete.Forum forum = Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
            if (forum == null)
            {
                throw new Exception(String.Format("No forum with ForumID = {0}.", forumID));
            }
            forum.ThreadsCount = forum.ThreadsCount == 0 ? 0 : forum.ThreadsCount - 1;
            Entities.ObjectStateManager.ChangeObjectState(forum, EntityState.Modified);
            Entities.SaveChanges();
        }

        public void IncreaseForumPostCount(int forumID)
        {
            // update thread posts
            Concrete.Forum forum = Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
            if (forum == null)
            {
                throw new Exception(String.Format("No forum with ForumID = {0}.", forumID));
            }
            forum.PostsCount++;
            Entities.ObjectStateManager.ChangeObjectState(forum, EntityState.Modified);
            Entities.SaveChanges();
        }

        public void IncreaseForumPostCount(int forumID, int postNumber)
        {
            // update thread posts
            Concrete.Forum forum = Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
            if (forum == null)
            {
                throw new Exception(String.Format("No forum with ForumID = {0}.", forumID));
            }
            forum.PostsCount = postNumber + forum.PostsCount;
            Entities.ObjectStateManager.ChangeObjectState(forum, EntityState.Modified);
            Entities.SaveChanges();
        }

        public void DecreaseForumPostCount(int forumID)
        {
            // update thread posts
            Concrete.Forum forum = Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
            if (forum == null)
            {
                throw new Exception(String.Format("No forum with ForumID = {0}.", forumID));
            }
            forum.PostsCount = forum.PostsCount == 0 ? 0 : forum.PostsCount - 1;
            Entities.ObjectStateManager.ChangeObjectState(forum, EntityState.Modified);
            Entities.SaveChanges();
        }

        public void DecreaseForumPostCount(int forumID, int postNumber)
        {
            // update thread posts
            Concrete.Forum forum = Entities.Forums.Where(f => f.ForumID == forumID).SingleOrDefault();
            if (forum == null)
            {
                throw new Exception(String.Format("No forum with ForumID = {0}.", forumID));
            }
            forum.PostsCount = forum.PostsCount - postNumber; 
            if(forum.PostsCount < 0)
            {
                forum.PostsCount = 0; 
            }
            Entities.ObjectStateManager.ChangeObjectState(forum, EntityState.Modified);
            Entities.SaveChanges();
        }

        /// <summary>
        /// Deletion of forum 
        /// </summary>
        /// <param name="forumID"></param>
        public void DeleteForum(int forumID)
        {
            Concrete.Forum forum = GetForum(forumID);
            // delete and save changes
            Entities.DeleteObject(forum);
            Entities.SaveChanges();
        }

        #region Utility
        /// <summary>
        /// Get max order for a forum level.
        /// </summary>
        /// <param name="parentId"></param>
        /// <returns>-1 if no result, otherwise max order property</returns>
        private int getMaxOrder(int parentId)
        {
            int order = -1;
            var orderQueryResult = (from f in Entities.Forums
                                    where f.ParentForumID == parentId
                                    select f.Order);
            if (orderQueryResult.Count() > 0)
            {
                order = orderQueryResult.Max();
            }
            return order;
        } 
        #endregion
    }
}
